#ifndef _PACKETWRITER_H_
#define _PACKETWRITER_H_

#include <sstream>
#include <map>
#include <queue>

#include <util/Thread.h>

class Packet;
class PacketListener;
class PacketFilter;
class PacketInterceptor;
class XMPPConnection;


/**
 * Writes packets to a XMPP server. Packets are sent using a dedicated thread. Packet
 * interceptors can be registered to dynamically modify packets before they're actually
 * sent. Packet listeners can be registered to listen for all outgoing packets.
 *
 * @author Matt Tucker
 */
class PacketWriter {
public:

	/**
     * Creates a new packet writer with the specified connection.
     *
     * @param connection the connection.
     */
	PacketWriter(XMPPConnection * connection) ;

    /** 
    * Initializes the writer in order to be used. It is called at the first connection and also 
    * is invoked if the connection is disconnected by an error.
    */ 
    void init() ;

    /**
     * Sends the specified packet to the server.
     *
     * @param packet the packet to send.
     */
    void sendPacket(Packet * packet);
	
    /**
     * Registers a packet listener with this writer. The listener will be
     * notified immediately after every packet this writer sends. A packet filter
     * determines which packets will be delivered to the listener. Note that the thread
     * that writes packets will be used to invoke the listeners. Therefore, each
     * packet listener should complete all operations quickly or use a different
     * thread for processing.
     *
     * @param packetListener the packet listener to notify of sent packets.
     * @param packetFilter the packet filter to use.
     */
    void addPacketListener(PacketListener * packetListener, PacketFilter * packetFilter) ;
	
    /**
     * Removes a packet listener.
     *
     * @param packetListener the packet listener to remove.
     */
    void removePacketListener(PacketListener * packetListener) ;

    /**
     * Returns the number of registered packet listeners.
     *
     * @return the count of packet listeners.
     */
    int getPacketListenerCount() ;
	
    /**
     * Registers a packet interceptor with this writer. The interceptor will be
     * notified of every packet that this writer is about to send. Interceptors
     * may modify the packet to be sent. A packet filter determines which packets
     * will be delivered to the interceptor.
     *
     * @param packetInterceptor the packet interceptor to notify of packets about to be sent.
     * @param packetFilter the packet filter to use.
     */
    void addPacketInterceptor(PacketInterceptor * packetInterceptor, PacketFilter * packetFilter) ;
	
    /**
     * Removes a packet interceptor.
     *
     * @param packetInterceptor the packet interceptor to remove.
     */
    void removePacketInterceptor(PacketInterceptor * packetInterceptor) ;
	
    /**
     * Starts the packet writer thread and opens a connection to the server. The
     * packet writer will continue writing packets until {@link #shutdown} or an
     * error occurs.
     */
    void startup() ; 
	
    /**
     * Sends to the server a new stream element. This operation may be requested several times
     * so we need to encapsulate the logic in one place. This message will be sent while doing
     * TLS, SASL and resource binding.
     *
     * @throws IOException If an error occurs while sending the stanza to the server.
     */
    void openStream();

    /**
     * Starts the keep alive process. A white space (aka heartbeat) is going to be
     * sent to the server every 30 seconds (by default) since the last stanza was sent
     * to the server.
     */
    void startKeepAliveProcess() ;

    void setStream(std::ostream * dataStream);

    /**
     * Shuts down the packet writer. Once this method has been called, no further
     * packets will be written to the server.
     */
    void shutdown() ;
	
    /**
     * Cleans up all resources used by the packet writer.
     */
    void cleanup() ;

private:
	class WriterThread : public Thread {
	public:
		WriterThread(PacketWriter * packetWriter);
		virtual void run(void * arg);
	private:
		PacketWriter * packetWriter;
	};

	class KeepAliveTask : public Thread {
	public:
		KeepAliveTask(PacketWriter * packetWriter, int delay); 
	protected:
		void run(void * arg) ;
	private:
		PacketWriter * packetWriter;
		int delay;
	};

    /**
     * A wrapper class to associate a packet filter with a listener.
     */
    class ListenerWrapper {
	public:
		ListenerWrapper(PacketListener * packetListener, PacketFilter * packetFilter); 

        void notifyListener(Packet * packet);
	private:
		PacketListener * packetListener;
        PacketFilter * packetFilter;
    };
	    
	/**
     * A wrapper class to associate a packet filter with an interceptor.
     */
    class InterceptorWrapper {
	public:
		InterceptorWrapper(PacketInterceptor * packetInterceptor, PacketFilter * packetFilter);

        bool equals(/* Object object */) {
			return true;
        }

        void notifyListener(Packet * packet); 
		
	private: 
		PacketInterceptor * packetInterceptor;
        PacketFilter * packetFilter;
    };

    /**
     * Returns the next available packet from the queue for writing.
     *
     * @return the next packet for writing.
     */
    Packet * nextPacket() ;

	void writePackets(Thread * thisThread) ;

    /**
     * Process listeners.
     *
     * @param packet the packet to process.
     */
    void processListeners(Packet * packet) ;
	
    /**
     * Process interceptors. Interceptors may modify the packet that is about to be sent.
     * Since the thread that requested to send the packet will invoke all interceptors, it
     * is important that interceptors perform their work as soon as possible so that the
     * thread does not remain blocked for a long period.
     *
     * @param packet the packet that is going to be sent to the server
     */
    void processInterceptors(Packet * packet) ;
;

	Thread * writerThread;
	Thread * keepAliveThread;
	//Writer * writer;  replace with the std::stringstream
	std::ostream * writer;
	XMPPConnection * connection;
	std::queue<Packet*> queue;
	bool done;
	std::map<PacketListener*, ListenerWrapper*> listeners;

    /**
     * Timestamp when the last stanza was sent to the server. This information is used
     * by the keep alive process to only send heartbeats when the connection has been idle.
	 * System.currentTimeMillis()
     */
    long lastActive ;

    /**
     * List of PacketInterceptors that will be notified when a new packet is about to be
     * sent to the server. These interceptors may modify the packet before it is being
     * actually sent to the server.
     */
	std::map<PacketInterceptor*, InterceptorWrapper*> interceptors ;

};

#endif // -- _PACKETWRITER_H_
